<template>
  <div class="wrapCon">
    <div class="number-animation">
      Type a number: <input v-model.number="number" />
      <p class="numCon">{{ tweened.number.toFixed(0) }}</p>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref, reactive, watch, toRefs } from 'vue'
  import myPackage from 'rspace_npm_demo'

  // 数字动效
  import gsap from 'gsap'
  const number = ref(0)
  const tweened = reactive({
    number: 0
  })
  watch(number, n => {
    gsap.to(tweened, { duration: 0.5, number: Number(n) || 0 })
  })

  // =======================================  函数参数解构 =======================================

  const user = reactive<object>({
    firstName: 'xu',
    lastName: 'qinping'
  })
  function getFullName({ firstName, lastName }: { firstName: string; lastName: string }): string {
    return firstName + lastName
  }

  const result = getFullName(user)
  console.log(result, 'result')

  // ======================================= rest参数 =======================================

  function add(...rests: any[]) {
    console.log(rests, 'rests')
    let amount = 0
    for (const item of rests) {
      amount += item
    }
    return amount
  }
  console.log(add(1, 2, 3), 'amount') //6

  // ======================================= setTimeout和setInterval实现倒计时=======================================
  // setTimeout实现倒计时
  function countDown(count: number) {
    setTimeout(() => {
      count--
      console.log(count, 'setTimeout 倒计时')
      if (count > 0) {
        countDown(count)
      }
    }, 1000)
  }
  // countDown(10)

  // setInterval实现倒计时 - 需手动清除定时器
  function setIntervalFun() {
    let count = 10
    let timer = setInterval(() => {
      count--
      console.log(count, 'setInterval 倒计时')
      if (count <= 0) {
        clearInterval(timer)
        timer = null
      }
    }, 1000)
  }

  // setIntervalFun()

  // ======================================= 分割线 =======================================

  // toRefs 使解构获取响应性
  const state = reactive({
    count: 0,
    name: 'John'
  })

  const { count } = toRefs(state)
  state.count++
  console.log(state.count, 'state.count')
  console.log(count.value, 'count')
  // console.log(count, 'count') // 不加value打印的是 RefImpl 类；猜测是 Proxy 监听基本数据类型的话，会需要一层对象作为包裹

  // ======================================= 分割线 =======================================

  // ref中定义的变量被reactive引用，不需要用 .value 获取
  const myName = ref<string>('我是铁锤')
  const myState = reactive({
    name: myName,
    age: 18
  })
  console.log(myState.name, 'ref中定义的变量被reactive引用，不需要用 .value获取')

  // ======================================= 使用自己的npm包 去除字符串中的空格 =======================================
  // 调用 RemoveSpace 方法
  const spaceStr = ' i 5  444'
  const resultspaceStr = myPackage.RemoveSpace(spaceStr)
  console.log(resultspaceStr, 'resultspaceStr')

  // ======================================= 函数声明和函数表达式 =======================================

  // console.log(sumFun(10, 10))  //会报错 因为是函数表达式，没有函数声明提升，执行上下文中没有 sumFun 函数
  let sumFun = function (num1: number, num2: number) {
    return num1 + num2
  }

  // ======================================= try...catch 只能捕捉到 同步执行 代码块中的错误 =======================================
  // 错误1
  try {
    setTimeout(() => {
      console.log('setTimeout error')
    }, 2000)
  } catch (err) {
    console.log(err)
  }
  // 错误2
  try {
    Promise.resolve().then(() => {
      console.log('error')
    })
  } catch (err) {
    console.log(err)
  }

  // 正确的做法是在异步操作中直接处理错误，例如使用回调函数、Promises或者async/await结合try...catch
  // 针对错误1解决
  new Promise((relove, reject) => {
    setTimeout(() => {
      try {
        console.log('new Promise error')
      } catch (err) {
        reject(err)
      }
    }, 2000)
  })
    .then(() => {
      // 正常执行时的处理逻辑
    })
    .catch(err => {
      console.log(err)
    }) // 这里会捕捉到错误})

  // 针对错误2解决 方法1
  Promise.resolve()
    .then(() => {
      console.log('Promise error')
    })
    .catch(err => {
      console.log(err) // 这里会捕捉到错误
    })
  // 针对错误2解决 方法2
  async function handleError() {
    try {
      await Promise.resolve().then(() => {
        console.log('await error')
      })
    } catch (err) {
      console.log(err) // 这里会捕捉到错误
    }
  }
  handleError()

  // ======================================= 函数 arguments =======================================
  function funArguments() {
    console.log(arguments.length, 'arguments.length')
  }
  funArguments(1, 2, 3)

  // ======================================= 递归阶乘函数 =======================================

  //严格模式下访问 arguments.callee 报错
  // function factorrial(num: number): number {
  //   if (num <= 1) {
  //     return 1
  //   } else {
  //     // return num * factorrial(num - 1)
  //     return num * arguments.callee(num - 1)
  //   }
  // }
  // 使用函数表达式可以
  let factorrial = function f(num: number): number {
    if (num <= 1) {
      return 1
    } else {
      return num * f(num - 1)
    }
  }
  console.log(factorrial(5), '递归阶乘函数')
  let anotherFactorrial = factorrial
  factorrial = null
  console.log(anotherFactorrial(4))

  // ======================================= 尾递归优化 只保留一个调用记录 复杂度O(1)=======================================
  // 递归本质上是一种循环操作，循环可以用递归代替，而一旦使用递归，就最好使用尾递归。

  function factorrialRecursion1(num: number, total: number): number {
    if (num === 1) return total
    return factorrialRecursion(num - 1, num * total)
  }
  console.log(factorrialRecursion1(5, 1), '尾递归优化') //阶乘函数 factorrialRecursion 需要用到一个中间变量total，那就把这个中间变量改写成函数的参数,就是不太直观

  // 使用 ES6 的函数默认值 优化尾递归。
  function factorrialRecursion(num: number, total = 1): number {
    if (num === 1) return total
    return factorrialRecursion(num - 1, num * total)
  }
  console.log(factorrialRecursion(5), '默认值优化尾递归')
</script>

<style lang="less" scoped>
  .number-animation {
    padding: 50px;
    .numCon {
      margin-top: 50px;
    }
  }
</style>
